//
//  KyoTransitioningInteractive.swift
//  test1
//
//  Created by Kyo on 2019/9/27.
//  Copyright © 2019 Kyo. All rights reserved.
//

import UIKit

// MARK: -------------------------
// MARK: Const/Enum/Struct

extension KyoTransitioningInteractive {
    
    /**< 常量 */
    public struct Const {
        /** 小于这个时间都算快速滑动并执行侧滑 */
        public static let defaultDuration: TimeInterval = 0.25
        //达到push/pop的临界百分比 50%
        public static let defaultPercentage: CGFloat = 0.5
    }
    
}

public class KyoTransitioningInteractive: NSObject {
 
    // MARK: ------------------------- Propertys
    
    /** 当前控制器 */
    public weak var viewController: UIViewController?
    /** push到的控制器 */
    public weak var pushToViewController: UIViewController?
    /** 操作类型 push或pop */
    public var operation: KyoTransitioningAnimation.Operation = .pop
    /** 动画类型 */
    public var type: KyoTransitioningAnimation.AnimationType?
    /** 手势 */
    public var gestureRecogniser: KyoTransitioningPanGestureRecognizer = KyoTransitioningPanGestureRecognizer()
    /** 交互实例 */
    public var interactive: UIPercentDrivenInteractiveTransition = UIPercentDrivenInteractiveTransition()
    /** 滑动手势最小时长（小于等于这个值算作快速滑动，执行pop） */
    public var duration: TimeInterval = Const.defaultDuration
    /** 达到push/pop的临界百分比（慢速滑动，滑动距离超出这个百分比说明达到pop状态） */
    public var percentage: CGFloat = Const.defaultPercentage
    
    /** 手势开始的时间戳 */
    private var interval: TimeInterval = 0
    
    // MARK: ------------------------- CycLife
    
    /** 返回交互手势实例，如果控制器或动画类型为nil，则返回nil */
    public class func generate(controller: UIViewController?,
                        pushToController: UIViewController?,
                        operation: KyoTransitioningAnimation.Operation = .pop,
                        type: KyoTransitioningAnimation.AnimationType?,
                        duration: TimeInterval = Const.defaultDuration,
                        percentage: CGFloat = Const.defaultPercentage) -> KyoTransitioningInteractive? {
        guard let type = type else {
            return nil
        }
        
        let interactive: KyoTransitioningInteractive = KyoTransitioningInteractive()
        interactive.viewController = controller
        interactive.pushToViewController = pushToController
        interactive.type = type
        interactive.duration = duration
        interactive.percentage = percentage
        switch operation {
        case .push where interactive.pushToViewController != nil:
            interactive.gestureRecogniser.addTarget(interactive, action: #selector(pushPanGestureRecognizer(_:)))
            interactive.gestureRecogniser.delegate = interactive
        case .pop where interactive.viewController != nil:
            interactive.gestureRecogniser.addTarget(interactive, action: #selector(popPanGestureRecognizer(_:)))
            interactive.gestureRecogniser.delegate = interactive
        case .none, .push, .pop: break
        @unknown default: break
        }
        interactive.viewController?.view.addGestureRecognizer(interactive.gestureRecogniser)
        return interactive
    }
    
    deinit {
        if self.viewController?.view.gestureRecognizers?.contains(self.gestureRecogniser) ?? false {
            self.viewController?.view.removeGestureRecognizer(self.gestureRecogniser)
        }
    }
    
    override public init() {
        super.init()

        self.interactive.completionCurve = .linear //使得手势关闭平滑（手指放开达到关闭临界点，自动关闭时平滑动画）
    }
}

// MARK: ------------------------- Methods

extension KyoTransitioningInteractive {
    
    /** 当前是否在处理pop手势交互中 */
    public func isInGesture() -> Bool {
        return self.gestureRecogniser.state == .began || self.gestureRecogniser.state == .changed
    }
    
}

// MARK: ------------------------- Gesture

extension KyoTransitioningInteractive {
    
    @objc public func popPanGestureRecognizer(_ recognizer: KyoTransitioningPanGestureRecognizer) {
        var translation: CGPoint = recognizer.translation(in: recognizer.view)
        let transform: CATransform3D? = recognizer.view?.layer.transform
        translation = CGPoint(x: translation.x * (transform?.m11 ?? 1),
                              y: translation.y * (transform?.m22 ?? 1))
        var currentPercentage: CGFloat = 0
        let viewSize: CGSize = recognizer.view?.bounds.size ?? CGSize.zero
        switch self.type {
        case .right:
            currentPercentage = max(-translation.x / viewSize.width, 0)
        case .left:
            currentPercentage = max(translation.x / viewSize.width, 0)
        case .top:
            currentPercentage = max(translation.y / viewSize.height, 0)
        case .bottom:
            currentPercentage = max(-translation.y / viewSize.height, 0)
        case .scaleToFull, .alpha, .none:
            let hPercentage: CGFloat = max(translation.x / viewSize.width, 0)
            let vPercentage: CGFloat = max(translation.y / viewSize.height, 0)
            currentPercentage = max(hPercentage, vPercentage)
        }
        
        switch recognizer.state {
        case .began:
            self.interval = Date().timeIntervalSince1970
            self.viewController?.navigationController?.popViewController(animated: true)
        case .changed:
            self.interactive.update(currentPercentage)
        case .ended:
            if (currentPercentage >= self.percentage) ||
                (Date().timeIntervalSince1970 - self.interval <= self.duration && currentPercentage > 0.1) {
                self.interactive.finish()
                let userInfo: [String: Any] = [KyoTransitioning.NotificationKey.fromController: self.viewController ?? UIViewController()]
                NotificationCenter.default.post(name: KyoTransitioning.NotificationName.popFinish, object: nil, userInfo: userInfo)
            } else {
                self.interactive.cancel()
            }
        default:
            self.interactive.cancel()
        }
    }

    @objc public func pushPanGestureRecognizer(_ recognizer: KyoTransitioningPanGestureRecognizer) {
        guard let pushToViewController = self.pushToViewController else {return}

        var translation: CGPoint = recognizer.translation(in: recognizer.view)
        let transform: CATransform3D? = recognizer.view?.layer.transform
        translation = CGPoint(x: translation.x * (transform?.m11 ?? 1),
                              y: translation.y * (transform?.m22 ?? 1))
        var currentPercentage: CGFloat = 0
        let viewSize: CGSize = recognizer.view?.bounds.size ?? CGSize.zero
        switch self.type {
        case .right:
            currentPercentage = max(-translation.x / viewSize.width, 0)
        case .left:
            currentPercentage = max(translation.x / viewSize.width, 0)
        case .top:
            currentPercentage = max(translation.y / viewSize.height, 0)
        case .bottom:
            currentPercentage = max(-translation.y / viewSize.height, 0)
        case .scaleToFull, .alpha, .none:
            let hPercentage: CGFloat = max(translation.x / viewSize.width, 0)
            let vPercentage: CGFloat = max(translation.y / viewSize.height, 0)
            currentPercentage = max(hPercentage, vPercentage)
        }
        
        switch recognizer.state {
        case .began:
            self.interval = Date().timeIntervalSince1970
            self.viewController?.navigationController?.pushViewController(pushToViewController, animated: true)
        case .changed:
            self.interactive.update(currentPercentage)
        case .ended:
            if (currentPercentage >= self.percentage) ||
                (Date().timeIntervalSince1970 - self.interval <= self.duration && currentPercentage > 0.1) {
                self.interactive.finish()
                let userInfo: [String: Any] = [KyoTransitioning.NotificationKey.fromController: self.viewController ?? UIViewController(),
                                               KyoTransitioning.NotificationKey.toController: self.pushToViewController ?? UIViewController()]
                NotificationCenter.default.post(name: KyoTransitioning.NotificationName.pushFinish, object: nil, userInfo: userInfo)
            } else {
                self.interactive.cancel()
            }
        default:
            self.interactive.cancel()
        }
    }
    
}

// MARK: ------------------------- UIGestureRecognizerDelegate

extension KyoTransitioningInteractive: UIGestureRecognizerDelegate {
    
    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard let gesture = gestureRecognizer as? KyoTransitioningPanGestureRecognizer else {return false}
        let object: KyoTransitioningInteractive? = gesture.fristGestureTarget() as? KyoTransitioningInteractive
        guard object == self else { return false}
        
        let translation = gesture.translation(in: gesture.view)
        switch self.type {
        case .right:
            return translation.x < 0
        case .left:
             return translation.x > 0
        case .top:
             return translation.y > 0
        case .bottom:
            return translation.y < 0
        case .scaleToFull, .alpha, .none:
            switch self.operation {
            case .push:
                return false
            case .pop:
                return true
            default: return false
            }
        }
    }
    
}





public class KyoTransitioningPanGestureRecognizer: UIPanGestureRecognizer {
    
    ///找到手势的第一个target
    public func fristGestureTarget() -> Any? {
        guard let targetsIvar = class_getInstanceVariable(self.classForCoder, ("_targets" as NSString).utf8String!) else {return nil}
        guard let targetActionPairs = object_getIvar(self, targetsIvar) as? [Any] else {return nil}
        guard let targetActionPairClass = NSClassFromString("UIGestureRecognizerTarget") else {return nil}
        guard let targetIvar = class_getInstanceVariable(targetActionPairClass, ("_target" as NSString).utf8String!) else {return nil}
        return object_getIvar(targetActionPairs.first, targetIvar)
    }
    
}
